package com.disys.dao;

import java.io.Serializable;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.NoResultException;
import javax.persistence.Persistence;
import javax.persistence.Query;
import javax.persistence.criteria.CriteriaQuery;

import org.hibernate.SQLQuery;
import org.hibernate.mapping.Array;

import com.disys.common.IProcessStatus;
import com.disys.model.Process;

public abstract class GenericDAO<T> implements Serializable {
	private static final long serialVersionUID = 1L;

	private static final EntityManagerFactory emf = Persistence.createEntityManagerFactory("JSFCrudPU");
	private static EntityManager em;

	private Class<T> entityClass;

	
	static{
		em = emf.createEntityManager();
	}
	
	public void beginTransaction() {
		if(em==null || !(em.isOpen())){
			em = emf.createEntityManager();
		}
		em.getTransaction().begin();
	}

	public void commit() {
		em.getTransaction().commit();
	}

	public void rollback() {
		em.getTransaction().rollback();
	}

	public void closeTransaction() {
		em.close();
	}

	public void commitAndCloseTransaction() {
		commit();
		closeTransaction();
	}

	public void flush() {
		em.flush();
	}

	public void joinTransaction() {
		em = emf.createEntityManager();
		em.joinTransaction();
	}

	public GenericDAO(Class<T> entityClass) {
		this.entityClass = entityClass;
	}

	public void save(T entity) {
		em.persist(entity);
	}

//	public void delete(T entity) {
//		T entityToBeRemoved = em.refresh(entity);
//
//		em.remove(entityToBeRemoved);
//	}

	public T update(T entity) {
		return em.merge(entity);
	}

	public T find(int entityID) {
		return em.find(entityClass, entityID);
	}

	public T findReferenceOnly(int entityID) {
		return em.getReference(entityClass, entityID);
	}

	// Using the unchecked because JPA does not have a
	// em.getCriteriaBuilder().createQuery()<T> method
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public List<T> findAll() {
		CriteriaQuery cq = em.getCriteriaBuilder().createQuery();
		cq.select(cq.from(entityClass));
		return em.createQuery(cq).getResultList();
	}

	// Using the unchecked because JPA does not have a
	// query.getSingleResult()<T> method
	@SuppressWarnings("unchecked")
	protected T findOneResult(String namedQuery, Map<String, Object> parameters) {
		T result = null;

		try {
			Query query = em.createNamedQuery(namedQuery);

			// Method that will populate parameters if they are passed not null and empty
			if (parameters != null && !parameters.isEmpty()) {
				populateQueryParameters(query, parameters);
			}

			result = (T) query.getSingleResult();

		} catch (NoResultException e) {
			System.out.println("No result found for named query: " + namedQuery);
		} catch (Exception e) {
			System.out.println("Error while running query: " + e.getMessage());
			e.printStackTrace();
		}

		return result;
	}

	private void populateQueryParameters(Query query, Map<String, Object> parameters) {
		for (Entry<String, Object> entry : parameters.entrySet()) {
			query.setParameter(entry.getKey(), entry.getValue());
		}
	}
	
	public List<T> findFinalizeRecords(String object,Integer isFinalized,BigInteger processId){
		em = getEntManager();
		//Query query = em.createQuery("Select sm from "+object+" sm where sm.finalized=:arg1 and sm.processId=:arg2");
		//query.setParameter("arg1", isFinalized);
		Query query = em.createQuery("Select sm from "+object+" sm where sm.processId=:arg1");
		query.setParameter("arg1", processId);
		return query.getResultList();
	}
	
	public List<T> findSupportingValues(String object, String recordType){
		em = getEntManager();
		Query query = em.createQuery("Select sm from "+object+" sm where sm.recordType=:arg1");
		query.setParameter("arg1", recordType);
		return query.getResultList();
	}
	
	
	public List<T> findAllSorted(String object){
		em = getEntManager();	
		try{
			Query query = em.createQuery("Select sm from "+object+" sm order by id desc");
			return query.getResultList();
		}catch(IllegalStateException e){
			e.printStackTrace();
			return null;
		}
		
	}
	
	public void finalizeRecords(String object,Integer isFinalized,BigInteger processId){
		Query query = em.createQuery("update  "+object+" sm set sm.finalized=:arg1 where sm.processId=:arg2");
		query.setParameter("arg1", isFinalized);
		query.setParameter("arg2", processId);
		query.executeUpdate();
	}
	
	public void updateReferences(String object,String columnName, String columnValue,String recordType){
		Query query = em.createQuery("update  "+object+" sm set sm.columnValue=:columnValue where sm.columnName=:columnName and sm.recordType=:recordType");
		query.setParameter("columnValue", columnValue);
		query.setParameter("columnName", columnName);
		query.setParameter("recordType", recordType);
		query.executeUpdate();
	}
	
	@SuppressWarnings("unchecked")
	public T getLastFinalizedProcess(String object){
		T result = null;
		try {
			Query query = em.createQuery("Select sm from  "+object+" sm where sm.week=(select max(t.week) from "+object+" t where t.status = '"+IProcessStatus.FINALIZED+"')");
			result = (T) query.getSingleResult();
		} catch (NoResultException e) {
			System.out.println("No result found for query: ");
		} catch (Exception e) {
			System.out.println("Error while running query: " + e.getMessage());
			e.printStackTrace();
		}
		return result;
	}
	
	private EntityManager getEntManager(){
		if(em==null || !(em.isOpen())){
			em = emf.createEntityManager();
			return em;
		}
		return em;
	}
	
	@SuppressWarnings("unchecked")
	public List<Integer> getAllProcessIds(String object){
		Query q = em.createQuery("Select sm.id from  "+object+" sm ");
		List<Integer> list = (List<Integer>) q.getResultList();
//		Iterator<Integer> iter = l.iterator();
//		List<Integer> list = new ArrayList<Integer>();
//		while (iter.hasNext()) {
//			Integer c = (Integer) iter.next();
//			list.add(c);
//		}
		return list;
	}
	
	@SuppressWarnings("unchecked")
	public List<T> findSelectedProcessRecords(String object,BigInteger processId){
		em = getEntManager();
		Query query = em.createQuery("Select sm from "+object+" sm where sm.processId=:arg1");
		query.setParameter("arg1", processId);
		return query.getResultList();
	}
	
	
	@SuppressWarnings("unchecked")
	public List<Integer> getFinalizeProcessOfAWeek(String object,Integer week){
		Query q = em.createQuery("Select sm.id from  "+object+" sm where sm.week=:arg1 and sm.status='"+IProcessStatus.FINALIZED+"'");
		q.setParameter("arg1", week);
		List<Integer> list = (List<Integer>) q.getResultList();
//		Iterator<Integer> iter = l.iterator();
//		List<Integer> list = new ArrayList<Integer>();
//		while (iter.hasNext()) {
//			Integer c = (Integer) iter.next();
//			list.add(c);
//		}
		return list;
	}
}
